class MVVM{
    constructor(options){
        this.$el = options.el;
        this.$data = options.data;
        if(this.$el){
            //数据劫持
            new Observer(this.$data);
            console.log(this.$data);
            //编译 模板+数据
            new Compile(this.$el,this);
        }
    }
}

class Dep{
    constructor(){
        this.watchers = [];
    }

    add(watcher){
        this.watchers.push(watcher)
    }

    notify(){
        this.watchers.forEach(watcher => {
            watcher.update();
        })
    }
}

//观察者
class Watcher{
    constructor(vm,expr,cb){
        this.vm = vm;
        this.expr = expr;
        this.cb = cb;
        //获取上一次的值
        this.oldValue = this.get();
    }

    get(){
        Dep.target = this;
        let value = compileUtil.getVal(this.expr,this.vm);
        Dep.target = null;
        return value
    }

    update(){
        let newValue = compileUtil.getVal(this.expr,this.vm);
        if(newValue != this.oldValue){
            this.cb(newValue);
        }
    }
}

//vm.$watch(vm,'user.username',(newValue) => {})

//数据劫持
class Observer{
    constructor(data){
        this.observer(data);
    }

    observer(data){
        //判断data是否存在，并且是否是对象
        if(data && typeof data === 'object'){
            // let data = {user:{username:'lili'},info:18}
            for(let key in data){
                //劫持
                this.observerData(data,key,data[key]);
            }
        }
    }

    //数据劫持
    observerData(data,key,value){
        this.observer(value);
        let dep = new Dep();
        Object.defineProperty(data,key,{
            set(newValue){
                console.log("设置值");
                if(newValue != value){
                    value = newValue;
                    dep.notify();
                }
            },
            get(){
                //添加观察者
                Dep.target && dep.add(Dep.target);
                console.log("获取值")
                return value
            }
        })
    }
}

//编译
class Compile{
    constructor(el,vm){
        this.vm = vm;
        this.el = this.isElementNode(el) ? el : document.querySelector(el);

        //把html节点放到内存中
        let fragment = this.nodeToElement(this.el);
        console.log(fragment);
        //编译之后
        this.compile(fragment);

        //把编译之后的结果放到dom树内
        this.el.appendChild(fragment);
    }

    //判断属性是否是指令 v-
    isDirective(attr){
        return attr.startsWith('v-')
    }

    //元素上查找指令
    compileElement(node){
        //获取元素节点的所有属性
        let attributes = node.attributes;
        [...attributes].forEach(attr => {
            // console.dir(attr);
            //name:属性名 v-model  expr:属性值 user.username
            let {name,value:expr} = attr; //value起的别名叫expr
            if(this.isDirective(name)){
                //属性是指令  v-model v-html v-text
                let [,directive] = name.split('-');
                compileUtil[directive](node,expr,this.vm);
            }
        })
    }
    //{{}}
    compileText(node){
        //获取文本的内容
        let content = node.textContent;
        //判断是否含有{{}}
        if(/\{\{(.+?)\}\}/.test(content)){ //{{user.username}}
            compileUtil.content(node,content,this.vm);
        }
    }

    //编译
    compile(node){
        let childnodes = node.childNodes;
        console.log(childnodes);
        [...childnodes].forEach(child => {
            //1.判断当前的元素还是文本
            if(this.isElementNode(child)){
                //元素节点
                this.compileElement(child);
                this.compile(child);
            }else{
                //文本节点
                this.compileText(child);
            }
        })
    }

    //把html节点放到内存中
    nodeToElement(node){
        //创建一个文档片段 它不再主dom树内，在内存里
        //所以将子元素插入到文档片段时不会引起页面回流
        let fragment = document.createDocumentFragment();

        let firstChild;
        //循环把node的子节点放到fragment文档片段内
        while(firstChild = node.firstChild){
            fragment.appendChild(firstChild);
        }

        return fragment
    }

    //判断是否是元素
    isElementNode(node){
        //元素
        return node.nodeType === 1
    }
}

//工具类
let compileUtil = {
    //获取表达式的值,从vm.$data查找user . username的值
    getVal(expr,vm){
        //[user,username]
        /**
         * {
                user:{
                    username:'lili'
                },
                info:'1709A'
            }
         * 
        */
        return expr.split('.').reduce((pre,current) => {
            return pre[current]
        },vm.$data)
    },

    model(node,expr,vm){ //v-model
        //从vm.$data查找user . username的值，给node节点value赋值
        let value = this.getVal(expr,vm);
        // console.log(value);
        let fn = this.updater.compileModel;
        //观察者
        new Watcher(vm,expr,(newValue) => {
            fn(node,newValue);
        })
        fn(node,value);
    },
    //{{expr}} //{{user.username}} ===> user.username
    content(node,content,vm){
        let value = content.replace(/\{\{(.+?)\}\}/g,(...argv) => {
            console.log(argv[1])
            //观察者
            new Watcher(vm,argv[1],(newValue) => {
                fn(node,newValue);
            })
            return this.getVal(argv[1],vm)
        })
        let fn = this.updater.compileContent;
        fn(node,value);
    },
    text(){

    },
    html(){

    },
    updater:{
        compileModel(node,value){
            node.value = value;
        },
        compileContent(node,value){
            node.textContent = value;
        }
    }
}